package com.huitong.gateway.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.huitong.common.core.domain.Result;
import com.huitong.gateway.filter.CacheRequestFilter;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashSet;
import java.util.function.Function;

/**
 * WebFlux工具类
 *
 * @author fromdrowning
 * @date 2024/3/17 15:35
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class WebFluxUtils {

    /**
     * 获取原请求路径
     *
     * @param exchange 当前请求和响应的信息
     * @return {@link String} 原请求路径
     */
    public static String getOriginalRequestUrl(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        LinkedHashSet<URI> uris = exchange.getAttributeOrDefault(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR, new LinkedHashSet<>());
        URI requestUri = uris.stream()
            .findFirst()
            .orElse(request.getURI());

        return UriComponentsBuilder.fromPath(requestUri.getRawPath()).build().toUriString();
    }

    /**
     * 是否为json请求
     *
     * @param exchange 当前请求和响应的信息
     * @return boolean 是json请求true 不是json请求false
     */
    public static boolean isJsonRequest(ServerWebExchange exchange) {
        String header = exchange.getRequest()
            .getHeaders()
            .getFirst(HttpHeaders.CONTENT_TYPE);

        return StrUtil.startWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
    }

    /**
     * 从缓存中读取request内的body
     * <p>
     * 注意要求经过 {@link ServerWebExchangeUtils#cacheRequestBody(ServerWebExchange, Function)} 此方法创建缓存
     * 框架内已经使用 {@link CacheRequestFilter} 全局创建了body缓存
     *
     * @param exchange 当前请求和响应的信息
     * @return {@link String} 从缓存中读取request内的body
     */
    public static String resolveBodyFromCacheRequest(ServerWebExchange exchange) {
        Object obj = exchange.getAttributes()
            .get(ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR);
        if (ObjectUtil.isNull(obj)) {
            return null;
        }

        DataBuffer buffer = (DataBuffer) obj;
        try (DataBuffer.ByteBufferIterator iterator = buffer.readableByteBuffers()) {
            return StandardCharsets.UTF_8.decode(iterator.next()).toString();
        }
    }

    /**
     * 设置webflux模型响应
     *
     * @param response 响应体
     * @param value    响应内容
     * @return {@link Mono}<{@link Void}> 响应式对象
     */
    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value) {
        return webFluxResponseWriter(response, HttpStatus.OK, value, HttpStatus.INTERNAL_SERVER_ERROR.value());
    }

    /**
     * 设置webflux模型响应
     *
     * @param response 响应体
     * @param value    响应内容
     * @param code     状态码
     * @return {@link Mono}<{@link Void}> 响应式对象
     */
    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code) {
        return webFluxResponseWriter(response, HttpStatus.OK, value, code);
    }

    /**
     * 设置webflux模型响应
     *
     * @param response 响应体
     * @param status   http状态码
     * @param value    响应内容
     * @param code     状态码
     * @return {@link Mono}<{@link Void}> 响应式对象
     */
    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code) {
        return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);
    }

    /**
     * 设置webflux模型响应
     *
     * @param response    响应体
     * @param contentType 内容类型
     * @param status      http状态码
     * @param value       响应内容
     * @param code        状态码
     * @return {@link Mono}<{@link Void}> 响应式对象
     */
    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) {
        response.setStatusCode(status);
        response.getHeaders()
            .add(HttpHeaders.CONTENT_TYPE, contentType);

        Result<?> result = Result.error(code, value.toString());
        DataBuffer dataBuffer = response.bufferFactory().wrap(JSONUtil.toJsonStr(result).getBytes());

        return response.writeWith(Mono.just(dataBuffer));
    }
}
